package com.ruoyi.file.service;

import com.ruoyi.file.utils.UploadFileService;
import com.ruoyi.system.api.domain.SysFile;
import com.ruoyi.system.api.factory.RemoteFileFallbackFactory;
import org.apache.tomcat.util.http.fileupload.FileItem;
import org.apache.tomcat.util.http.fileupload.servlet.ServletFileUpload;
import org.apache.tomcat.util.http.fileupload.servlet.ServletRequestContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.UUID;

@Service
public class FileService {
    private static final Logger log = LoggerFactory.getLogger(RemoteFileFallbackFactory.class);

    @Value("${file-store.max-request-size}")
    private long sizeMax;
    @Value("${file-store.max-file-size}")
    private long fileSizeMax;
    @Value("${file-store.file-path}")
    private String filePath;
    @Autowired
    private UploadFileService uploadFileService;

    public SysFile save(String app, HttpServletRequest request) {
        String fileId = this.createFileId();
        File storeFile = new File(this.getFilePath(app, fileId));
        if (!storeFile.getParentFile().exists()) {
            storeFile.getParentFile().mkdirs();
        }
        MyFileItemFactory factory = new MyFileItemFactory(storeFile);

        ServletFileUpload upload = new ServletFileUpload();
        upload.setFileItemFactory(factory);
        upload.setFileSizeMax(fileSizeMax);//字节
        upload.setSizeMax(sizeMax);//字节

        try {
            List<FileItem> items = upload.parseRequest(new ServletRequestContext(request));
            if (items.isEmpty()) {
                return null;
            }
            String name = items.get(0).getName();
            SysFile sysFile = new SysFile();
            sysFile.setUrl(uploadFileService.getPathUrl(fileId,name));
            sysFile.setFileId(fileId);
            sysFile.setName(name);
            return sysFile;
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            storeFile.delete();
        }
        return null;
    }


    private static final String APPLICATION_PDF = "application/pfd";
    private static final String APPLICATION_WORD = "application/msword";
    private static final String APPLICATION_EXCEL = "application/vnd.ms-excel;charset=utf-8";

    public boolean download(String app, String fileId, String name, boolean isOnline,
                            HttpServletRequest request, HttpServletResponse response) {
        OutputStream out = null;
        name = name != null ? name : fileId;
        try {
            if (!isOnline)//不设置“Content-Disposition”时手机浏览器下载文件时可据文件后缀调用相关的系统工具
            {
                boolean isIE6 = request.getHeader("User-Agent") != null && request.getHeader("User-Agent").indexOf("MSIE") > 0;
                if (isIE6) {
                    response.setHeader("Content-Disposition", "attachment;filename=" +
                            new String(name.getBytes("gb2312"), "ISO8859-1"));
                } else {
                    String indexName = new String(name.getBytes(), StandardCharsets.ISO_8859_1);//URLEncoder.encode(document.getName(), "UTF-8");
                    response.setHeader("Content-disposition", "attachment;filename=" + indexName);
                }
            } else {
                response.setHeader("Content-Disposition", "inline; filename=" + name);
                int lastIndexOf = name.lastIndexOf(".");
                String ext = lastIndexOf < 0 ? null : name.substring(lastIndexOf + 1);
                String type = "application/octet-stream;charset=UTF-8";
                String dis = "inline; filename=" + name;
                if ("mp4".equals(ext)) {
                    type = "video/mp4;charset=UTF-8";
                } else if ("jpg".equals(ext)) {
                    type = "image/jpeg;charset=UTF-8";
                } else if ("gif".equals(ext) || "jpeg".equals(ext) || "png".equals(ext) || "tif".equals(ext) || "tiff".equals(ext)) {
                    type = "image/" + ext + ";charset=UTF-8";
                } else if ("txt".equals(ext)) {
                    type = "text/plain;charset=UTF-8";
                } else if (".pdf".equals(ext)) {
                    type = "application/pdf;charset=UTF-8";
                } else if ("ppt".equals(ext)) {
                    type = "application/ppt;charset=UTF-8";
                } else if ("xls".equals(ext) || "xlsx".equals(ext)) {
                    type = "application/vnd.ms-excel;charset=UTF-8";
                    dis = "attachment;filename=" + URLEncoder.encode(name, "utf-8");
                } else if ("doc".equals(ext) || "docx".equals(ext)) {
                    type = "application/msword;charset=UTF-8";
                    dis = "attachment;filename=" + URLEncoder.encode(name, "utf-8");
                }

                response.setContentType(type);
                response.setHeader("Content-disposition", dis);
//                response.setHeader("Content-Length", String.valueOf(file.length()));
            }
            response.setDateHeader("Last-Modified", System.currentTimeMillis());
            out = response.getOutputStream();
            long cursor = 0;
            long endCursor = 0;
            String range = request.getHeader("Range");
            if (range != null) {
                response.setStatus(206);//标识为下载部份内容，在线播放时可能会由于状态的设置不正常而无法播放
                try {
                    cursor = Integer.parseInt(range);
                } catch (Exception ignored) {
                }
                if (cursor == 0) {
                    try {
                        if (range.length() > 8) {
                            int _splitIndex = range.indexOf("-");
                            if (_splitIndex > 0 && _splitIndex < range.length() - 1) {
                                cursor = Long.parseLong(range.substring(6, _splitIndex));
                                endCursor = Long.parseLong(range.substring(_splitIndex + 1));
                            } else {
                                cursor = Integer.parseInt(range.substring(6, range.length() - 1));
                            }
                        }
                    } catch (Exception ignored) {
                    }
                }
            }
            File source = new File(this.getFilePath(app, fileId));
            if (source == null || !this.isValid(source) || source.length() == 0) {
                log.warn("({}){} not found!", app, fileId);
                response.setStatus(404);
                return false;
            }
            if (cursor >= source.length()) {
                log.warn("read file ({}){} failed!", app, fileId);
                response.setStatus(404);
                return false;
            }
            if (endCursor == 0 || endCursor < cursor) {
                endCursor = source.length() - 1;
            }
            response.setHeader("Content-Range", "bytes " + cursor + "-" + endCursor + "/" + source.length());
            response.setContentLength((int) (endCursor - cursor + 1));

            this.download(out, source, cursor, endCursor - cursor + 1);
            out.flush();
            return true;
        } catch (IOException e) {
            log.error("download ({}){} failed.-------{}", app, fileId, e.getMessage());
            return false;
        } finally {
            if (out != null) {
                try {
                    out.close();
                } catch (Exception ignored) {
                }
            }
        }
    }

    private void download(OutputStream bout, File source, long cursor, long limit) {
        try (FileChannel inChannel = new FileInputStream(source).getChannel()) {
            inChannel.position(cursor);
            ByteBuffer bb = ByteBuffer.allocate(1024 * 64);
            int _len = 0;
            while ((_len = inChannel.read(bb)) != -1) {
                bb.flip();
                if (_len > limit) {
                    _len = (int) limit;
                }
                limit -= _len;
                bout.write(bb.array(), 0, _len);
                bb.clear();
                if (limit == 0) {
                    break;
                }
            }
        } catch (IOException e) {
            log.error(e.getMessage());
        }
    }

    private boolean isValid(File file) {
        try {
            //防止路径注入
            return file.isFile() && file.getAbsolutePath().equals(file.getCanonicalPath());
        } catch (IOException e) {
            log.error(e.getMessage(), e);
            return false;
        }
    }

    public String getFilePath(String app, String fileId) {
        String yearMonth = fileId.substring(0, 4);
        String day = fileId.substring(4, 6);
        String uuid01 = fileId.substring(7, 9);
        String uuid23 = fileId.substring(9, 11);
        String uuid = fileId.substring(7);
        return String.format(filePath, app, yearMonth, day, uuid01, uuid23, uuid);
    }

    private String createFileId() {
        SimpleDateFormat sp = new SimpleDateFormat("yyMMdd");
        return sp.format(new Date()) + "_" + UUID.randomUUID().toString().replaceAll("-", "");
    }

    public Boolean deleteFile(String[] fileIds, String appId) {
        for (String fileId : fileIds) {
            File file = new File(this.getFilePath(appId, fileId));
            if (file.isFile() && file.exists()) {
                file.delete();
            }
        }
        return true;
    }
}
